/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package dale2507.gates;

import dale2507.gates.data.GateLoader;
import dale2507.gates.data.Settings;
import dale2507.gates.data.permissions.Permissions;
import dale2507.gates.exceptions.GateConnectionException;
import dale2507.gates.exceptions.GateDirectoryException;
import dale2507.gates.exceptions.PermissionException;
import dale2507.gates.gate.Gate;
import dale2507.gates.gate.GateSettings;
import dale2507.gates.gate.comparator.*;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;

/**
 *
 * @author Dale
 */
public class GateDirectory {
    
    private static GateDirectory instance = null;
    
    /**
     * Gets the instance of the gate directory containing the current gate list
     * @return An instance of the class
     */
    public static GateDirectory getInstance() {
        return GatePlugin.getInstance().getGateDirectory();
    }
    
    private ArrayList<Gate> gates = new ArrayList<>();
    
    GateDirectory() {
        
    }
    
    Gate[] getGates() {
        return gates.toArray(new Gate[0]);
    }
    
    /**
     * Gets a list of gates based on the person wanting the list. Returns a maximum of 20 gates.
     * @param cs Person sending the command
     * @param i Page number to return
     * @return List of gates which user is allowed to see
     */
    public Gate[] getGates(CommandSender cs, int i) {
        Gate[] stored = this.getGates();
        
        ArrayList<Gate> ga = new ArrayList<>();
        for (Gate g : stored) {
            if (cs instanceof Player) {
                try {
                    Player p = (Player) cs;
                    switch (g.getSettings().getVisibility()) {
                        case GateSettings.PUBLIC:
                            ga.add(g);
                            break;
                        case GateSettings.HIDDEN:
                            if (g.getSettings().getOwner().equals(p.getName())) {
                                ga.add(g);
                            } else if (Permissions.getPermissions().getCommandPermissions().allowGateListHidden(p)) {
                                ga.add(g);
                            }
                            break;
                        case GateSettings.PRIVATE:
                            // TODO permissions bypass
                            if (g.getSettings().getOwner().equals(((Player) cs).getName())) {
                                ga.add(g);
                            } else if (Permissions.getPermissions().getCommandPermissions().allowGateListPrivate(p)) {
                                ga.add(g);
                            }
                            break;
                    }
                } catch (PermissionException ex) {
                    Logger.getLogger(GateDirectory.class.getName()).log(Level.SEVERE, null, ex);
                }
            } else {
                ga.add(g);
            }
            if (ga.size() == 15) break;
        }
        return ga.toArray(new Gate[0]);
    }
    
    /**
     * Gets a single gate from the list
     * @param comparator The comparator to use to find the gate
     * @param item Object to use in the comparator to find a gate with the same object
     * @return The gate containing the object, null if none contain the object.
     */
    public Gate getGate(IGateComparator comparator, Object item) {
        for (Gate g : this.getGates()) {
            if (comparator.check(g, item)) return g;
        }
        return null;
    }

    /**
     * Adds a gate to the gate directory
     * @param g The gate to be added to the directory
     * @throws GateDirectoryException Thrown if a gate already exists containing the same properties
     */
    public void addGate(Gate g) throws GateDirectoryException {
        if (this.getGate(new GateNameComparator(), g.getSettings().getName()) != null)
            throw new GateDirectoryException("A gate with that name already exists");
        if (g.getSettings().getAddress() != null && !g.getSettings().getAddress().isEmpty()) {
            if (this.getGate(new GateAddressComparator(), g.getSettings().getAddress()) != null) 
                throw new GateDirectoryException("A gate with that address already exists");
        } else {
            // Gate is outgoing, check if allowed
            if (!Settings.getInstance().creation.allowOutgoingOnly())
                throw new GateDirectoryException("Outgoing only gates are not allowed");
        }
        if (this.getGate(new GateRingComparator(), g.getRing()) != null)
            throw new GateDirectoryException("A gate with that ring already exists");
        if (this.getGate(new GateDisplayComparator(), g.getRing()) != null)
            throw new GateDirectoryException("A gate is already using that sign");
        if (this.getGate(new GateDHDComparator(), g.getDhd()) != null)
            throw new GateDirectoryException("A gate is already using that chest");
        
        gates.add(g);
        GateLoader.getInstance().saveGate(g);
        Logger.getLogger(GateDirectory.class.getName()).log(Level.INFO, g.getSettings().getName() + " gate added to directory");
    }
    
    public void deleteGate(Gate g) {
        try {
            g.getActivator().deactivate(null);
        } catch (PermissionException ex) {
            Logger.getLogger(GateDirectory.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        gates.remove(g);
        GateLoader.getInstance().deleteGate(g);
        g.removeListeners();
        Logger.getLogger(GateDirectory.class.getName()).log(Level.INFO, g.getSettings().getName() + " gate removed from the directory");
    }

    /**
     * Loop through gates dialling indefinite connection ones
     */
    public void dialIndefiniteGates() {
        if (Settings.getInstance().creation.indefiniteConnectionItem() != null) {
            for (Gate g : this.getGates()) {
                if (g.getSettings().isIndefinite()) {
                    try {
                        g.getActivator().activate(null);
                    } catch (PermissionException | GateConnectionException ex) {
                        Logger.getLogger(Gate.class.getName()).log(Level.WARNING, g.getSettings().getName() + " gate: " + ex.getMessage());
                    }
                }
            }
        }
    }
    
}
